package roborally.model;

import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.HashSet;
import java.util.Set;

import exception.IllegalPositionException;
import exception.IsTerminatedException;
import be.kuleuven.cs.som.annotate.*;
import be.kuleuven.cs.som.taglet.*;

import roborally.IFacade;
import roborally.model.items.*;
import roborally.script.ScriptReader;

public class Facade implements IFacade<Board, Robot, Wall, Battery, RepairKit, SurpriseBox> {

	@Override
	public Board createBoard(long width, long height) {
		return new Board(width, height);
	}

	@Override
	public void merge(Board board1, Board board2) {
		board1.merge(board2);
		
	}

	@Override
	public Battery createBattery(double initialEnergy, int weight) {
		return new Battery(initialEnergy, weight);
	}

	@Override
	public void putBattery(Board board, long x, long y, Battery battery) {
		
		Position batteryPosition = new Position(x, y, board.getWidth(), board.getHeight());
		battery.setBoardPosition(board, batteryPosition);
		
	}

	@Override
	public long getBatteryX(Battery battery) throws IllegalStateException {
		if (battery.getPosition() == null) throw new IllegalStateException();
		return battery.getPosition().getX();
	}

	@Override
	public long getBatteryY(Battery battery) throws IllegalStateException {
		if (battery.getPosition() == null) throw new IllegalStateException();
		return battery.getPosition().getY();
	}

	@Override
	public Robot createRobot(int orientation, double initialEnergy) {
		Orientation robotOrientation = Orientation.UP;
		for(Orientation or : Orientation.values()){
			if(or.intValue() == orientation){
				robotOrientation = or;
			}
		}
		return new Robot(robotOrientation, initialEnergy, 20000);
	}

	@Override
	public void putRobot(Board board, long x, long y, Robot robot) {
		robot.setBoardPosition(board, new Position(x,y));

	}

	@Override
	public long getRobotX(Robot robot) throws IllegalStateException {
		if( robot.getPosition() == null) throw new IllegalStateException();
		return robot.getPosition().getX();
	}

	@Override
	public long getRobotY(Robot robot) throws IllegalStateException {
		if( robot.getPosition() == null) throw new IllegalStateException();
		return robot.getPosition().getY();
	}

	@Override
	public int getOrientation(Robot robot) {
		return robot.getOrientation().intValue();
	}

	@Override
	public double getEnergy(Robot robot) {
		return robot.getEnergy().getValueIn(Energy.Unit.WS);
	}

	@Override
	public void move(Robot robot) {
		try {
			robot.move();
		} catch (Exception e) {
			System.out.println("Error. " + e.getMessage());
		}
	}

	@Override
	public void turn(Robot robot) {
		robot.turn(Direction.CLOCKWISE);
		
	}

	@Override
	public int isMinimalCostToReach17Plus() {
		return 1;
	}

	@Override
	public double getMinimalCostToReach(Robot robot, long x, long y) {
		Position pos = new Position(x, y);
		return robot.getMinimalCostToReach(pos);
	}

	@Override
	public int isMoveNextTo18Plus() {
		return 1;
	}

	@Override
	public void moveNextTo(Robot robot, Robot other) {
		robot.moveNextTo(other);
		
	}

	@Override
	public void shoot(Robot robot) throws UnsupportedOperationException {
		robot.shoot();
		
	}

	@Override
	public Wall createWall() throws UnsupportedOperationException {
		return new Wall();
	}

	@Override
	public void putWall(Board board, long x, long y, Wall wall)
			throws UnsupportedOperationException {
		wall.setBoardPosition(board,  new Position(x, y));
		
	}

	@Override
	public long getWallX(Wall wall) throws IllegalStateException,
			UnsupportedOperationException {
		if( wall.getPosition() == null) throw new IllegalStateException();
		return wall.getPosition().getX();
	}

	@Override
	public long getWallY(Wall wall) throws IllegalStateException,
			UnsupportedOperationException {
		if( wall.getPosition() == null) throw new IllegalStateException();
		return wall.getPosition().getY();
	}

	@Override
	public Set<Robot> getRobots(Board board) {
		Set<Robot> result = new HashSet<Robot>();
		for(Robot robot : board.getElements(Robot.class)) {
			result.add(robot);
		}
		return result;
	}

	@Override
	public Set<Battery> getBatteries(Board board) {
		Set<Battery> result = new HashSet<Battery>();
		for(Battery battery : board.getElements(Battery.class)) {
			result.add(battery);
		}
		return result;
	}

	@Override
	public Set<Wall> getWalls(Board board) throws UnsupportedOperationException {
		Set<Wall> result = new HashSet<Wall>();
		for(Wall wall : board.getElements(Wall.class)) {
			result.add(wall);
		}
		return result;
	}

	@Override
	public RepairKit createRepairKit(double repairAmount, int weight) {
		
		return new RepairKit(weight, repairAmount);
	}

	@Override
	public void putRepairKit(Board board, long x, long y, RepairKit repairKit) {
		repairKit.setBoardPosition(board, new Position(x, y));
		
	}

	@Override
	public long getRepairKitX(RepairKit repairKit) throws IllegalStateException {
		if( repairKit.getPosition() == null) throw new IllegalStateException();
		return repairKit.getPosition().getX();
	}

	@Override
	public long getRepairKitY(RepairKit repairKit) throws IllegalStateException {
		if( repairKit.getPosition() == null) throw new IllegalStateException();
		return repairKit.getPosition().getY();
	}

	@Override
	public SurpriseBox createSurpriseBox(int weight) {
		
		return new SurpriseBox(weight);
	}

	@Override
	public void putSurpriseBox(Board board, long x, long y,
			SurpriseBox surpriseBox) {
		surpriseBox.setBoardPosition(board, new Position(x, y));
		
	}

	@Override
	public long getSurpriseBoxX(SurpriseBox surpriseBox)
			throws IllegalStateException {
		if (surpriseBox.getPosition() == null) throw new IllegalStateException();
		return surpriseBox.getPosition().getX();
	}

	@Override
	public long getSurpriseBoxY(SurpriseBox surpriseBox)
			throws IllegalStateException {
		if (surpriseBox.getPosition() == null) throw new IllegalStateException();
		return surpriseBox.getPosition().getY();
	}

	@Override
	public void pickUpBattery(Robot robot, Battery battery) {
		robot.pickUp(battery);
		
	}

	@Override
	public void useBattery(Robot robot, Battery battery) {
		robot.use(battery);
		
	}

	@Override
	public void dropBattery(Robot robot, Battery battery) {
		robot.drop(battery);
		
	}

	@Override
	public void pickUpRepairKit(Robot robot, RepairKit repairKit) {
		robot.pickUp(repairKit);
		
	}

	@Override
	public void useRepairKit(Robot robot, RepairKit repairKit) {
		robot.use(repairKit);
		
	}

	@Override
	public void dropRepairKit(Robot robot, RepairKit repairKit) {
		robot.drop(repairKit);
		
	}

	@Override
	public void pickUpSurpriseBox(Robot robot, SurpriseBox surpriseBox) {
		robot.pickUp(surpriseBox);
		
	}

	@Override
	public void useSurpriseBox(Robot robot, SurpriseBox surpriseBox) {
		robot.use(surpriseBox);
		
	}

	@Override
	public void dropSurpriseBox(Robot robot, SurpriseBox surpriseBox) {
		robot.drop(surpriseBox);
		
	}

	@Override
	public void transferItems(Robot from, Robot to) {
		from.transferItemsToRobot(to);
		
	}

	@Override
	public Set<RepairKit> getRepairKits(Board board) {
		
		return board.getElements(RepairKit.class);
	}

	@Override
	public Set<SurpriseBox> getSurpriseBoxes(Board board) {

		return board.getElements(SurpriseBox.class);
	}

	@Override
	public int loadProgramFromFile(Robot robot, String path) {
		try {
			robot.loadProgram(path);
			return 0;
		} catch (Exception e) {
			return -1;
		}
	}

	@Override
	public int saveProgramToFile(Robot robot, String path) {
		try {
			FileWriter writer = new FileWriter(path+".prog");
			writer.write(ScriptReader.statementToString(robot.getProgram(), 1));
			writer.flush();
			return 0;
		} catch (IOException e) {
			return -1;
		}
	}

	@Override
	public void prettyPrintProgram(Robot robot, Writer writer) {
		try {
			writer.write(ScriptReader.statementToString(robot.getProgram(),1));
		} catch (IOException e) {}
	}

	@Override
	public void stepn(Robot robot, int n) {
		for(int i = 0; i<n; i++){
			robot.stepProgram();
		}
		
	}

}
